from enum import Enum
import numpy as np


class Trace(Enum):
    LEAVE_LEFT = 1
    JUMPOUT_LEFT = 2
    LEAVE_RIGHT = 3
    JUMPOUT_RIGHT = 4


class Kernal:
    def __init__(self):
        self.memSize = 3000000
        self.code = None
        self.inp = None
        self.forw = None
        self.backw = None
        self.mem = None
        self.save = None
        self.trace = None
        self.output = None
        self.dataPointer = 0
        self.codePointer = 0
        self.inputPointer = 0
        self.forwLock = False
        self.backwLock = True

    def load(self, code: str, inp: str):
        self.code = code
        assert (self.code != '')
        self.inp = inp

    def analyze(self):
        def analyze_(pos: int, dep: int):
            i = pos
            while i < len(self.code):
                if self.code[i] == '[':
                    i = analyze_(i + 1, dep + 1)
                elif self.code[i] == ']':
                    assert (pos != 0)
                    self.forw[pos - 1] = i
                    self.backw[i] = pos - 1
                    return i
                i += 1
            assert (dep == 0)

        self.forw = {}
        self.backw = {}
        analyze_(0, 0)

    def start(self):
        self.mem = np.zeros(self.memSize, np.uint8)
        self.save = []
        self.trace = []
        self.output = ''
        self.dataPointer = 0
        self.codePointer = 0
        self.inputPointer = 0
        self.forwLock = False
        self.backwLock = True

    def step_forw(self):
        if self.forwLock:
            return False

        comm = self.code[self.codePointer]
        if comm == '+':
            self.mem[self.dataPointer] += 1
            self.codePointer += 1
        elif comm == '-':
            self.mem[self.dataPointer] -= 1
            self.codePointer += 1
        elif comm == '<':
            self.dataPointer -= 1
            self.codePointer += 1
        elif comm == '>':
            self.dataPointer += 1
            self.codePointer += 1
        elif comm == ',':
            self.save.append(self.mem[self.dataPointer])
            if self.inputPointer < len(self.inp):
                self.mem[self.dataPointer] = ord(self.inp[self.inputPointer])
            else:
                self.mem[self.dataPointer] = 0
            self.inputPointer += 1
            self.codePointer += 1
        elif comm == '.':
            self.output += chr(self.mem[self.dataPointer])
            self.codePointer += 1
        elif comm == '[':
            if self.mem[self.dataPointer] != 0:
                self.trace.append(Trace.LEAVE_LEFT)
                self.codePointer += 1
            else:
                self.trace.append(Trace.JUMPOUT_LEFT)
                self.codePointer = self.forw[self.codePointer] + 1
        elif comm == ']':
            if self.mem[self.dataPointer] == 0:
                self.trace.append(Trace.LEAVE_RIGHT)
                self.codePointer += 1
            else:
                self.trace.append(Trace.JUMPOUT_RIGHT)
                self.codePointer = self.backw[self.codePointer] + 1
        else:
            self.codePointer += 1

        self.backwLock = False
        if self.codePointer >= len(self.code):
            self.forwLock = True
            self.codePointer = len(self.code)
        return True

    def step_backw(self):

        if self.backwLock:
            return False

        comm = self.code[self.codePointer - 1]
        if comm == '[' or comm == ']':
            tr = self.trace.pop()
            if tr == Trace.LEAVE_LEFT:
                self.codePointer -= 1
            elif tr == Trace.JUMPOUT_LEFT:
                self.codePointer = self.backw[self.codePointer - 1]
            elif tr == Trace.LEAVE_RIGHT:
                self.codePointer -= 1
            elif tr == Trace.JUMPOUT_RIGHT:
                self.codePointer = self.forw[self.codePointer - 1]
            return True

        if comm == '+':
            self.mem[self.dataPointer] -= 1
            self.codePointer -= 1
        elif comm == '-':
            self.mem[self.dataPointer] += 1
            self.codePointer -= 1
        elif comm == '<':
            self.dataPointer += 1
            self.codePointer -= 1
        elif comm == '>':
            self.dataPointer -= 1
            self.codePointer -= 1
        elif comm == ',':
            self.mem[self.dataPointer] = self.save.pop()
            self.inputPointer -= 1
            self.codePointer -= 1
        elif comm == '.':
            self.output = self.output[:-1]
            self.codePointer -= 1
        else:
            self.codePointer -= 1

        self.forwLock = False
        if self.codePointer <= 0:
            self.backwLock = True
            self.codePointer = 0
        return True
